#include "sniffer.h"
#include "misc.h"
#include <string>
#include <QTime>
#include <QTextCodec>
#include <QTextStream>

Sniffer::Sniffer(){}
Sniffer::~Sniffer(){
    pcap_freealldevs(alldevs);
}

bool Sniffer::doing = false;
const QStringList Sniffer::fileSuffix = {"*.d"};

QList<QString> Sniffer::getAdapters(){
    pcap_if_t *d;
    int i=0;
    QList<QString> ads;
    char errbuf[PCAP_ERRBUF_SIZE];
    /* Load Npcap and its functions. */
    if (!LoadNpcapDlls())
    {
        qDebug()<<"Couldn't load Npcap\n";
        return ads;
    }
    /* Retrieve the device list from the local machine */
    //会获取所有的网络适配器 包括虚拟设备
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,
                            NULL /* auth is not needed */,
                            &alldevs, errbuf) == -1)
    {
        qDebug()<<"Error in pcap_findalldevs_ex: "<<errbuf<<"\n";
        return ads;
    }

    /* Print the list */
    for(d= alldevs; d != NULL; d= d->next)
    {
        QString a = QObject::tr("%1. %2");
        //qDebug()<<
        a.arg(++i).arg(d->name);
        a.clear();
        a=" (%1)";
        if (d->description){
            a = a.arg(d->description);
            //qDebug()<<a;
            ads.append(a.remove(" (Network adapter '").remove("' on local host)"));
        }
        else
            qDebug()<<" (No description available)\n";
    }

    if (i == 0)
    {
        qDebug()<<"\nNo interfaces found! Make sure Npcap is installed.\n";
    }
    return ads;
}

void Sniffer::beginSniff(pcap_if_t *alldevs, int inum){
    pcap_t *adhandle;
    pcap_if_t *d;
    pcap_dumper_t *dumpfile;
    char errbuf[PCAP_ERRBUF_SIZE];
    struct pcap_pkthdr *header;
    const u_char *pkt_data;
    int packetsCount=0;
    qDebug()<<"inum="<<++inum;

    /* Jump to the selected adapter */
    int i=0;
    for(d=alldevs; i< inum-1 ;d=d->next, i++);
    /* Open the device */
    qDebug()<<"Begin Sniff in "<<d->description;
    if ( (adhandle= pcap_open(d->name, // name of the device
                  65536, // portion of the packet to capture
                         // 65536 guarantees that the whole packet
                         // will be captured on all the link layers
                  PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
                  100, // read timeout
                  NULL, // authentication on the remote machine
                  errbuf // error buffer
                  ) ) == NULL)
    {
      qDebug()<<QObject::tr("\nUnable to open the adapter. %1 is not supported by Npcap\n").arg(d->name);
      /* Free the device list */
      return;
    }
    qDebug()<<d->description<<" has been opened successfully";
    std::string time = QTime::currentTime().toString("hh.mm.ss.zzz").toStdString();
    /* Open the dump file */
    dumpfile = pcap_dump_open(adhandle, (std::string() + "dump_" + time+".d").c_str());

    if(dumpfile==NULL)
    {
        qDebug()<<"\nError opening output file\n";
        return;
    }
    qDebug()<<"\nlistening on "<<d->description;

    /* Retrieve the packets */
    int res = 0;
    while(doing&&(res = pcap_next_ex( adhandle, &header, &pkt_data)) >= 0){
        if(res == 0)
            /* Timeout elapsed */
            continue;
        //qDebug()<<"Received packet";
        pcap_dump((unsigned char *)dumpfile, header, pkt_data);
        //++packetsCount;

    }
    pcap_dump_close(dumpfile);
    qDebug()<<"dump file closed";

    if(res == -1){
        qDebug()<<"Error reading the packets: "<<pcap_geterr(adhandle);
        return;
    }
}

void Sniffer::processTCPInfo(QString & infos, const tcp_header* header){
    u_short flags = ntohs(header->flags);
    QString s;
    QTextStream info(&s);
    // 末尾6位 URG ACK PSH RST SYN FIN
    if(flags & 0x20)
        info << "URG,";
    if(flags & 0x10)
        info << "ACK,";
    if(flags & 0x8)
        info << "PSH,";
    if(flags & 0x4)
        info << "RST,";
    if(flags & 0x2)
        info << "SYN,";
    if(flags & 0x1)
        info << "FIN";
    if(s.endsWith(",")){
        s.remove(s.length()-1,1);
    }
    s.prepend("[");
    s.append("] ");
    // seq和ack为无符号4字节整型 且不是相对值
    s.append("Seq=").append(QString::number(ntohl(header->seq)));
    s.append(" Ack=").append(QString::number(ntohl(header->ack)));
    s.append(" Win=").append(QString::number(ntohs(header->winS)));
    // Win的值正确
    infos = s;
}
QString Sniffer::processData(QByteArray & data){
    QString qs;
    QTextStream s(&qs);
    s<<"Length in Bytes: "<<data.size()<<"\n";
    s<<"Raw Data:\n";
    for(int i=0; i<data.size();++i){
        int x = u_char(data[i]);
        s<<Qt::hex<<((x&0xf0)>>4)<<(x&0xf)<<" ";
    }
    s << "\n Interpreted (as UTF-8) Data:\n";
    QTextCodec *codecUTF8 = QTextCodec::codecForName("UTF-8");
    s << codecUTF8->toUnicode(data);
    return qs;
}
void Sniffer::readDump(QString fileName, QTableWidget * table, QStringList filters){
    pcap_t *fp;
    struct pcap_pkthdr *header;
    const u_char *pkt_data;
    int res;
    int len_ether;
    //问题代码 调试和直接运行时结果不同 必须要分开写！！！
    std::string s = fileName.toStdString();
    const char* dumpName=s.c_str();
    //问题代码 调试和直接运行时结果不同
    char errbuf[PCAP_ERRBUF_SIZE];
    char source[PCAP_BUF_SIZE];
    /* Create the source string according to the new Npcap syntax */
    if ( pcap_createsrcstr(  source, // variable that will keep the source string
                             PCAP_SRC_FILE,  // we want to open a file
                             NULL,      // remote host
                             NULL,      // port on the remote host
                             dumpName,    // name of the file we want to open
                             errbuf      // error buffer
                             ) != 0)
    {
        qDebug()<<"\nError creating a source string\n";
        return;
    }

    /* Open the capture file */
    if ( (fp= pcap_open(source, // name of the device
                        65536, // portion of the packet to capture
                        // 65536 guarantees that the whole packet
                        // will be captured on all the link layers
                        PCAP_OPENFLAG_PROMISCUOUS, // promiscuous mode
                        100, // read timeout
                        NULL, // authentication on the remote machine
                        errbuf // error buffer
                        ) ) == NULL)
    {
        qDebug()<<QObject::tr("Unable to open the file %1.").arg(source);
        return;
    }
    /* Check the link layer. We support only Ethernet for simplicity. */
    if(pcap_datalink(fp) != DLT_EN10MB)
    {
        qDebug()<<"\nNot on Ethernet networks.\n";
        len_ether = 4;
    }else {
        len_ether = 14;
    }

    /* Retrieve the packets */
    while((res = pcap_next_ex( fp, &header, &pkt_data)) >= 0){
        doRead(header,pkt_data,len_ether,table,filters);
    }

    if(res == -1){
        qDebug()<<"Error reading the packets: "<<pcap_geterr(fp);
        return;
    }

    return;
}

void Sniffer::doRead(struct pcap_pkthdr *header,const u_char *pkt_data,
                     int len_ether, QTableWidget * table,
                     QStringList filters){
    struct tm ltime;
    char timestr[16];
    ip_header *ih; //ip包起始位置
    udp_header *uh; //udp包起始位置
    tcp_header *th; //tcp包起始位置
    u_int ip_len; //ip包的首部长度
    u_char proto; //ip包的上层协议
    u_short sport,dport;
    time_t local_tv_sec;
    /* convert the timestamp to readable format */
    local_tv_sec = header->ts.tv_sec;
    localtime_s(&ltime, &local_tv_sec);
    strftime( timestr, sizeof timestr, "%H:%M:%S", &ltime);

    /* print timestamp and length of the packet */
    //qDebug()<<QObject::tr("Time=%1  len=%2 ").arg(timestr).arg(header->len);
    /* retireve the position of the ip header */
    ih = (ip_header *) (pkt_data +
                       len_ether); //length of ethernet header

    /* retireve the position of the udp header */
    ip_len = (ih->ver_ihl & 0xf) * 4;
    proto = ih->proto;
    QString proS;
    QString InfoTCP;
    QByteArray data;
    int tcp_len;
    int data_len = 0;
    if(proto == 6){ //上层为TCP
        proS = "tcp";
        th = (tcp_header *) ((u_char*)ih + ip_len);
        sport = ntohs( th->sport );
        dport = ntohs( th->dport );
        processTCPInfo(InfoTCP, th);
        u_short len=ntohs(th->flags);
        tcp_len = ((len&0xf000)>>12)*4;
        data_len = ntohs(ih->tlen)-ip_len-tcp_len;
        data = QByteArray((char *)th+tcp_len,data_len);
    }else if(proto == 17){ //上层为UDP
        proS = "udp";
        uh = (udp_header *) ((u_char*)ih + ip_len);
        sport = ntohs( uh->sport );
        dport = ntohs( uh->dport );
        data_len = ntohs(uh->len)-8;
        data = QByteArray((char *)uh+8,data_len);
    }else{ //其他协议
        proS = "Unknown: ";
        sport = 0;
        dport = 0;
    }
    if(data.size()!=data_len){
        qDebug()<<"Error in data_len";
    }
    QString source = QObject::tr("%1.%2.%3.%4")
                          .arg(ih->saddr.byte1)
                          .arg(ih->saddr.byte2)
                          .arg(ih->saddr.byte3)
                          .arg(ih->saddr.byte4);
    QString destination = QObject::tr("%1.%2.%3.%4")
                          .arg(ih->daddr.byte1)
                          .arg(ih->daddr.byte2)
                          .arg(ih->daddr.byte3)
                          .arg(ih->daddr.byte4);
    QString s_port=QString::number(sport);
    QString d_port=QString::number(dport);
    if(filters[1]=="..."||source==filters[1]){
        if(filters[3]=="..."||destination==filters[3]){
            if(filters[2]=="-1"||s_port==filters[2]){
                if(filters[4]=="-1"||d_port==filters[4]){
                    if(filters[0]=="ip"||filters[0].contains(proS)){
                        //满足所有条件 将此数据包添加到表格中
                        //获取旧的行计数，表格末尾加入一个新空行，有新的空行才能设置新行条目
                        int nOldRowCount = table->rowCount();
                        table->insertRow(nOldRowCount);
                        QTableWidgetItem *item1 = new QTableWidgetItem(timestr);
                        table->setItem(nOldRowCount,0,item1);
                        QTableWidgetItem *item2 = new QTableWidgetItem(source+":"+s_port);
                        table->setItem(nOldRowCount,1,item2);
                        QTableWidgetItem *item3 = new QTableWidgetItem(destination+":"+d_port);
                        table->setItem(nOldRowCount,2,item3);
                        QTableWidgetItem *item4 = new QTableWidgetItem(proS);
                        table->setItem(nOldRowCount,3,item4);
                        QTableWidgetItem *item5 = new QTableWidgetItem(QString::number(header->len));
                        table->setItem(nOldRowCount,4,item5);
                        if(proto == 6){
                            QTableWidgetItem *item6 = new QTableWidgetItem(InfoTCP);
                            table->setItem(nOldRowCount, 5,item6);
                        }else {
                            QTableWidgetItem *item6 = new QTableWidgetItem(QString());
                            table->setItem(nOldRowCount, 5, item6);
                        }
                        if(proto == 6 || proto == 17){
                            QTableWidgetItem *item7 = new QTableWidgetItem(processData(data));
                            table->setItem(nOldRowCount, 6,item7);
                        }else {
                            QTableWidgetItem *item7 = new QTableWidgetItem(QString());
                            table->setItem(nOldRowCount, 6, item7);
                        }
                        qDebug()<<"add a row: "<<nOldRowCount;
                    }
                }
            }
        }
    }
//    qDebug()<<proS<<QObject::tr("%1.%2.%3.%4:%5")
//              .arg(ih->saddr.byte1)
//              .arg(ih->saddr.byte2)
//              .arg(ih->saddr.byte3)
//              .arg(ih->saddr.byte4)
//              .arg(sport)
//           <<"  ---->>>  "
//          <<QObject::tr("%1.%2.%3.%4:%5")
//            .arg(ih->daddr.byte1)
//            .arg(ih->daddr.byte2)
//            .arg(ih->daddr.byte3)
//            .arg(ih->daddr.byte4)
//            .arg(dport);
}
